Reverse Proxy Patterns

Common reverse proxy design patterns for self-hosted services and internal platforms

created: Sat Mar 14 2026 00:00:00 GMT+0000 (Coordinated Universal Time) updated: Sat Mar 14 2026 00:00:00 GMT+0000 (Coordinated Universal Time) #reverse-proxy#networking#self-hosting

Introduction

A reverse proxy accepts client requests and forwards them to upstream services. It commonly handles TLS termination, host-based routing, request header forwarding, and policy enforcement in front of self-hosted applications.

Purpose

Reverse proxies are used to:

  • Publish multiple services behind one or a few public entry points
  • Centralize TLS certificates
  • Apply authentication, authorization, or rate-limiting controls
  • Simplify backend service placement and migration

Architecture Overview

Typical request flow:

Client -> Reverse proxy -> Upstream application

Common proxy responsibilities:

  • TLS termination and certificate management
  • Routing by hostname, path, or protocol
  • Forwarding of Host, client IP, and other headers
  • Optional load balancing across multiple backends

Common Patterns

Edge proxy for many internal services

One proxy handles traffic for multiple hostnames:

  • grafana.example.com
  • gitea.example.com
  • vault.example.com

This is a good default for small homelabs and internal platforms.

Internal proxy behind a VPN

Administrative services are reachable only through a private network such as Tailscale, WireGuard, or a dedicated management VLAN. This reduces public attack surface.

Path-based routing

Useful when hostnames are limited, but more fragile than host-based routing because some applications assume they live at /.

Dynamic discovery proxy

Tools such as Traefik can watch container metadata and update routes automatically. This reduces manual config for dynamic container environments, but it also makes label hygiene and network policy more important.

Configuration Example

NGINX example:

server {
    listen 443 ssl http2;
    server_name app.example.com;
 
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8080;
    }
}

Caddy example:

app.example.com {
    reverse_proxy 127.0.0.1:8080
}

Troubleshooting Tips

Application redirects to the wrong URL

  • Check forwarded headers such as Host and X-Forwarded-Proto
  • Verify the application's configured external base URL
  • Confirm TLS termination behavior matches application expectations

WebSocket or streaming traffic fails

  • Check proxy support for upgraded connections
  • Review buffering behavior if the application expects streaming responses

Backend works locally but not through the proxy

  • Verify the proxy can reach the upstream host and port
  • Check the proxy network namespace if running in a container
  • Confirm firewall rules permit the proxy-to-upstream path

Best Practices

  • Prefer host-based routing over deep path rewriting
  • Publish only the services that need an edge entry point
  • Keep proxy configuration under version control
  • Use separate internal and public entry points when trust boundaries differ
  • Standardize upstream headers and base URL settings across applications

References